#include "../include/Operation.h"

static const Complex cTRUE(1.0);
static const Complex cFALSE(0.0);

// Equality
static Complex eq_proc(const OperationData::ArgumentList&);
// Inequality
static Complex neq_proc(const OperationData::ArgumentList&);
// Boolean inverse
static Complex binv_proc(const OperationData::ArgumentList&);
// Or
static Complex or_proc(const OperationData::ArgumentList&);
// And
static Complex and_proc(const OperationData::ArgumentList&);
// Greater than
static Complex gt_proc(const OperationData::ArgumentList&);
// Greater than or equal to
static Complex gte_proc(const OperationData::ArgumentList&);
// Less than
static Complex lt_proc(const OperationData::ArgumentList&);
// Less than or equal to
static Complex lte_proc(const OperationData::ArgumentList&);
// If statement
static Complex if_proc(const OperationData::ArgumentList&);

////////////////////////////////////////////////////////////////////////////////
// EQUALITY
////////////////////////////////////////////////////////////////////////////////
static const QString eq_id("==");
static const QString eq_hs( \
"NAME:\n"
"== (Equality).\n\n"
"USAGE:\n"
"a==b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is equal to b, otherwise 0.\n"
);
static const bool eq_fn = false;
static const uint eq_pr = 0;
static const uint eq_na = 2;

static Complex eq_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != eq_na) {
        throw Operation::Exception(
          QString("eq() called with %1 arguments instead of %2.").
          arg(args.size()).arg(eq_na));
    }
    if (args.at(0) == args.at(1)) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// INEQUALITY
////////////////////////////////////////////////////////////////////////////////
static const QString neq_id("!=");
static const QString neq_hs( \
"NAME:\n"
"!= (Inequality).\n\n"
"USAGE:\n"
"a!=b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is not equal to b, otherwise 0.\n"
);
static const bool neq_fn = false;
static const uint neq_pr = 0;
static const uint neq_na = 2;

static Complex neq_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != neq_na) {
        throw Operation::Exception(
          QString("neq() called with %1 arguments instead of %2.").
          arg(args.size()).arg(neq_na));
    }
    if (args.at(0) != args.at(1)) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// BOOLEAN INVERSE
////////////////////////////////////////////////////////////////////////////////
static const QString binv_id("!");
static const QString binv_hs( \
"NAME:\n"
"! (Boolean inverse).\n\n"
"USAGE:\n"
"!a.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is 0, otherwise 0.\n"
);
static const bool binv_fn = false;
static const uint binv_pr = 0;
static const uint binv_na = 1;

static Complex binv_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != binv_na) {
        throw Operation::Exception(
          QString("binv() called with %1 arguments instead of %2.").
          arg(args.size()).arg(binv_na));
    }
    if (args.at(0) == cFALSE) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// OR
////////////////////////////////////////////////////////////////////////////////
static const QString or_id("||");
static const QString or_hs( \
"NAME:\n"
"|| (Boolean or).\n\n"
"USAGE:\n"
"a||b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is non-zero OR b is non-zero, otherwise 0.\n"
);
static const bool or_fn = false;
static const uint or_pr = 0;
static const uint or_na = 2;

static Complex or_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != or_na) {
        throw Operation::Exception(
          QString("or() called with %1 arguments instead of %2.").
          arg(args.size()).arg(or_na));
    }
    if (args.at(0) == cFALSE and args.at(1) == cFALSE) {
        return cFALSE;
    } else {
        return cTRUE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// AND
////////////////////////////////////////////////////////////////////////////////
static const QString and_id("&&");
static const QString and_hs( \
"NAME:\n"
"&& (Boolean and).\n\n"
"USAGE:\n"
"a&&b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is non-zero AND b is non-zero, otherwise 0.\n"
);
static const bool and_fn = false;
static const uint and_pr = 0;
static const uint and_na = 2;

static Complex and_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != and_na) {
        throw Operation::Exception(
          QString("and() called with %1 arguments instead of %2.").
          arg(args.size()).arg(and_na));
    }
    if (args.at(0) == cFALSE or args.at(1) == cFALSE) {
        return cFALSE;
    } else {
        return cTRUE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// GREATER THAN
////////////////////////////////////////////////////////////////////////////////
static const QString gt_id(">");
static const QString gt_hs( \
"NAME:\n"
"> (Greater than).\n\n"
"USAGE:\n"
"a>b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is greater than b, otherwise 0.\n"
"If a or b are complex numbers, only the real part is considered.\n"
);
static const bool gt_fn = false;
static const uint gt_pr = 0;
static const uint gt_na = 2;

static Complex gt_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != gt_na) {
        throw Operation::Exception(
          QString("gt() called with %1 arguments instead of %2.").
          arg(args.size()).arg(gt_na));
    }
    if (imag(args.at(0)) != 0.0 or imag(args.at(1)) != 0.0) {
        throw Operation::Exception("gt(): Comparison of complex numbers.");
    } else if (real(args.at(0)) > real(args.at(1))) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// GREATER THAN OR EQUAL TO
////////////////////////////////////////////////////////////////////////////////
static const QString gte_id(">=");
static const QString gte_hs( \
"NAME:\n"
">= (Greater than or equal).\n\n"
"USAGE:\n"
"a>=b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is greater than or equal to b, otherwise 0.\n"
"If a or b are complex numbers, only the real part is considered.\n"
);
static const bool gte_fn = false;
static const uint gte_pr = 0;
static const uint gte_na = 2;

static Complex gte_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != gte_na) {
        throw Operation::Exception(
          QString("gte() called with %1 arguments instead of %2.").
          arg(args.size()).arg(gte_na));
    }
    if (imag(args.at(0)) != 0.0 or imag(args.at(1)) != 0.0) {
        throw Operation::Exception("gte(): Comparison of complex numbers.");
    } else if (real(args.at(0)) >= real(args.at(1))) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// LESS THAN
////////////////////////////////////////////////////////////////////////////////
static const QString lt_id("<");
static const QString lt_hs( \
"NAME:\n"
"< (Less than).\n\n"
"USAGE:\n"
"a<b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is less than b, otherwise 0.\n"
"If a or b are complex numbers, only the real part is considered.\n"
);
static const bool lt_fn = false;
static const uint lt_pr = 0;
static const uint lt_na = 2;

static Complex lt_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != lt_na) {
        throw Operation::Exception(
          QString("lt() called with %1 arguments instead of %2.").
          arg(args.size()).arg(lt_na));
    }
    if (imag(args.at(0)) != 0.0 or imag(args.at(1)) != 0.0) {
        throw Operation::Exception("lt(): Comparison of complex numbers.");
    } else if (real(args.at(0)) < real(args.at(1))) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// LESS THAN OR EQUAL TO
////////////////////////////////////////////////////////////////////////////////
static const QString lte_id("<=");
static const QString lte_hs( \
"NAME:\n"
"<= (Less than or equal to).\n\n"
"USAGE:\n"
"a<=b.\n\n"
"DESCRIPTION:\n"
"Returns 1 if a is less than or equal to b, otherwise 0.\n"
"If a or b are complex numbers, only the real part is considered.\n"
);
static const bool lte_fn = false;
static const uint lte_pr = 0;
static const uint lte_na = 2;

static Complex lte_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != lte_na) {
        throw Operation::Exception(
          QString("lte() called with %1 arguments instead of %2.").
          arg(args.size()).arg(lte_na));
    }
    if (imag(args.at(0)) != 0.0 or imag(args.at(1)) != 0.0) {
        throw Operation::Exception("lte(): Comparison of complex numbers.");
    } else if (real(args.at(0)) <= real(args.at(1))) {
        return cTRUE;
    } else {
        return cFALSE;
    }
}

////////////////////////////////////////////////////////////////////////////////
// IF STATEMENT
////////////////////////////////////////////////////////////////////////////////
static const QString if_id("if");
static const QString if_hs( \
"NAME:\n"
"if.\n\n"
"USAGE:\n"
"if(condition, true_val, false_val).\n\n"
"DESCRIPTION:\n"
"Returns true_val if condition is true, otherwise false_val.\n"
);
static const bool if_fn = true;
static const uint if_pr = 10;
static const uint if_na = 3;

static Complex if_proc(const OperationData::ArgumentList& args)
{
    if (static_cast<uint>(args.size()) != if_na) {
        throw Operation::Exception(
          QString("if() called with %1 arguments instead of %2.").
          arg(args.size()).arg(if_na));
    }
    if (args.at(0) != cFALSE) {
        return args.at(1);
    } else {
        return args.at(2);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Add to operations list.
////////////////////////////////////////////////////////////////////////////////
static const Operation oeq(eq_id, eq_hs, eq_fn, eq_pr, eq_na, eq_proc);
static const Operation oneq(neq_id, neq_hs, neq_fn, neq_pr, neq_na, neq_proc);
static const Operation obinv(binv_id, binv_hs, binv_fn, binv_pr, binv_na,
                             binv_proc);
static const Operation oor(or_id, or_hs, or_fn, or_pr, or_na, or_proc);
static const Operation oand(and_id, and_hs, and_fn, and_pr, and_na, and_proc);
static const Operation ogt(gt_id, gt_hs, gt_fn, gt_pr, gt_na, gt_proc);
static const Operation ogte(gte_id, gte_hs, gte_fn, gte_pr, gte_na, gte_proc);
static const Operation olt(lt_id, lt_hs, lt_fn, lt_pr, lt_na, lt_proc);
static const Operation olte(lte_id, lte_hs, lte_fn, lte_pr, lte_na, lte_proc);
static const Operation oif(if_id, if_hs, if_fn, if_pr, if_na, if_proc);

